package ace.module.account.core.impl.service.callback.auth.handler.impl;

import ace.cmp.core.enums.SystemCodeEnum;
import ace.cmp.core.exception.BusinessException;
import ace.cmp.core.model.R;
import ace.cmp.json.api.JsonService;
import ace.cmp.spring.util.core.ValidatorUtils;
import ace.module.account.api.model.auth.dto.AuthenticationResult;
import ace.module.account.api.model.auth.input.AuthenticationParams;
import ace.module.account.core.impl.service.authentication.AuthenticationService;
import ace.module.account.core.impl.service.callback.auth.enums.AuthenticationTypeCallbackEnum;
import ace.module.account.core.impl.service.callback.auth.handler.Oauth2AuthenticationCallbackHandler;
import ace.module.account.core.impl.service.callback.auth.model.bo.Oauth2AuthenticationCallbackContext;
import ace.module.oauth2.server.api.model.dto.ext.Oauth2AceAuthResultDetailDto;
import ace.module.oauth2.server.api.model.dto.ext.Oauth2AceAuthResultDto;
import ace.module.oauth2.server.api.model.input.Oauth2AceAuthParams;
import lombok.AccessLevel;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;

/**
 * @author caspar
 * @date 2023/3/16 9:49
 */
public abstract class AbstractOauth2AuthenticationCallbackHandler<TOauth2AceAuthenticationCallbackParams extends Oauth2AceAuthParams, TAuthenticationParams extends AuthenticationParams>
    implements Oauth2AuthenticationCallbackHandler {

  private final AuthenticationTypeCallbackEnum authenticationTypeCallbackEnum;

  @Getter(value = AccessLevel.PROTECTED)
  private final Class<TAuthenticationParams> authenticationParamsClass;

  @Getter(value = AccessLevel.PROTECTED)
  private final Class<TOauth2AceAuthenticationCallbackParams> oauth2AceAuthenticationCallbackParamsClass;

  @Autowired
  @Getter(value = AccessLevel.PROTECTED)
  private AuthenticationService authenticationService;

  @Autowired
  @Getter(value = AccessLevel.PROTECTED)
  private JsonService jsonService;

  @Autowired
  @Getter(value = AccessLevel.PROTECTED)
  private ValidatorUtils validatorUtils;

  public AbstractOauth2AuthenticationCallbackHandler(AuthenticationTypeCallbackEnum authenticationTypeCallbackEnum) {
    this.authenticationTypeCallbackEnum = authenticationTypeCallbackEnum;
    ResolvableType resolvableType = ResolvableType.forType(this.getClass());
    this.oauth2AceAuthenticationCallbackParamsClass = (Class<TOauth2AceAuthenticationCallbackParams>) resolvableType.getSuperType().getGeneric(0).getRawClass();
    this.authenticationParamsClass = (Class<TAuthenticationParams>) resolvableType.getSuperType().getGeneric(1).getRawClass();
  }

  @Override
  public Oauth2AceAuthResultDto authentication(Oauth2AuthenticationCallbackContext context) {
    if (!this.support(context)) {
      return null;
    }
    TAuthenticationParams bizParams = this.convertCallbackParamsToAuthenticationParams(context);
    this.validatorUtils.validateThrowFirst(bizParams);
    String idOfClient = context.getBodyModel().getOauth2AuthenticationCallbackInput().getIdOfClient();
    Long iamAppId = context.getBodyModel().getIamAppId();
    String accountTag = context.getBodyModel().getAccountTag();
    String authenticationGrantType = this.authenticationTypeCallbackEnum.getAuthenticationGrantType().getCode();

    try {
      R<AuthenticationResult> authenticationResultR = this.authenticationService
          .authentication(idOfClient, iamAppId, authenticationGrantType, accountTag, bizParams);

      Oauth2AceAuthResultDto resultDto = new Oauth2AceAuthResultDto();
      if (authenticationResultR.success()) {
        Oauth2AceAuthResultDetailDto resultDetailDto = new Oauth2AceAuthResultDetailDto();
        resultDetailDto.setAccountId(authenticationResultR.getData().getAccountId().toString());
        resultDetailDto.setAttributes(authenticationResultR.getData().getAttributes());

        resultDto.setData(resultDetailDto);
      }
      resultDto.setCode(authenticationResultR.getCode());
      resultDto.setMessage(authenticationResultR.getMessage());
      resultDto.setExtras(authenticationResultR.getExtras());
      return resultDto;
    } catch (BusinessException ex) {
      Oauth2AceAuthResultDto resultDto = new Oauth2AceAuthResultDto();
      resultDto.setCode(ex.getCode());
      resultDto.setMessage(ex.getMessage());
      resultDto.setExtras(ex.getExtras());
      return resultDto;
    } catch (Exception ex) {
      Oauth2AceAuthResultDto resultDto = new Oauth2AceAuthResultDto();
      resultDto.setCode(SystemCodeEnum.ERROR_SYSTEM_EXCEPTION.getCode());
      resultDto.setMessage(ex.getMessage());
      return resultDto;
    }
  }

  protected TAuthenticationParams convertCallbackParamsToAuthenticationParams(Oauth2AuthenticationCallbackContext context) {
    Oauth2AceAuthParams oauth2AceAuthParamsMap = context.getBodyModel()
        .getOauth2AuthenticationCallbackInput()
        .getBizParams();

    String oauth2AceAuthParamsMapString = jsonService.toJson(oauth2AceAuthParamsMap);

    TOauth2AceAuthenticationCallbackParams oauth2AceAuthenticationCallbackParams = this.jsonService.toObject(oauth2AceAuthParamsMapString, this.getOauth2AceAuthenticationCallbackParamsClass());

    TAuthenticationParams authenticationParams = this.convertCallbackParamsToAuthenticationParams(context, oauth2AceAuthParamsMapString, oauth2AceAuthenticationCallbackParams);

    return authenticationParams;
  }

  protected TAuthenticationParams convertCallbackParamsToAuthenticationParams(
      Oauth2AuthenticationCallbackContext context,
      String oauth2AceAuthParamsMapString,
      TOauth2AceAuthenticationCallbackParams oauth2AceAuthenticationCallbackParams
  ) {
    return this.jsonService.toObject(oauth2AceAuthParamsMapString, this.getAuthenticationParamsClass());
  }

  @Override
  public boolean support(Oauth2AuthenticationCallbackContext context) {
    String authType = context.getBodyModel().getOauth2AuthenticationCallbackInput().getAuthType();
    return StringUtils.equals(this.authenticationTypeCallbackEnum.getCode(), authType);
  }
}
